﻿

#include "file.h"

#include "externs.h"

namespace hpl
{

File::File(const std::string& path, Program* program): path(path), isOpened(false), hasBom(false), encoding(File::UNDEFINED), currentLineNumber(0), Node(Node::FILE, program)
{
	if(Open(path))
	{
		Parse();
	}
}

bool File::Open(const std::string& path)
{
	const size_t nameLength = path.size();
	if(nameLength > 0)
	{
		//lookup for file name starting point:
		int delimIndex = -1;
		for(int i = (nameLength - 1); i >= 0; i--)
		{
			char currentSymbol = path[i];
			if(currentSymbol == '/' || currentSymbol == '\\')
			{
				delimIndex = i;
				break;
			}
		}
		
		if(delimIndex >= (nameLength - 1))
		{
			Globals::messenger.write(boost::format("E: File::File(): file's path \"%s\" cannot end with slash or backslash.\n") % path);
			return false;
		}
		
		//assuming the whole path to be the file's name:
		if(delimIndex < 0)
		{
			name = path;
		}
		else
		{
			name.assign(path, delimIndex + 1, nameLength);
		}
		
		inputStream.open(path);
		if(!inputStream)
		{
			Globals::messenger.write(boost::format("E: File::File(): file \"%s\" was NOT opened.\n"));
			return false;
		}
		
		isOpened = true;
		
		//find if file has utf8 encoding:
		istreambuf_iterator<char> it(inputStream.rdbuf());
		istreambuf_iterator<char> eos;
		//if does:
		if(utf8::is_valid(it, eos))
		{
			encoding = File::UTF8;
			
			//check if file has "Byte order mark":
			// get length of file:
			inputStream.seekg (0, ios::end);
			const int length = inputStream.tellg();
			inputStream.seekg (0, ios::beg);
			//if it's enaugh length to read at leas BOM:
			if(length >= 3)
			{
				if(inputStream.get() == 0xEF && inputStream.get() == 0xBB && inputStream.get() == 0xBF)
				{
					hasBom = true;
				}
				
				inputStream.seekg(0, ios:beg);
			}
		}
		//otherwise assume ANSI encoding (who use UTF 16,32?):
		else
		{
			encoding = File::ANSI;
		}
		
		return true;
	}
	else
	{
		Globals::messenger.write(boost::format("E: File::File(): input file path has zero length.\n"));
		return false;
	}
	
	return true;
}

void File::Parse()
{
	if(!isOpened)
	{
		return;
	}
	
	curState = File::UNDEFINED;
	
	//parse the whole file:
	while(!inputStream.eof())
	{
		char currentChar;
		inputStream.get(currentChar);
		
		Step(currentChar);
	}
}

void File::Step(const char token)
{
	switch(currentState)
	{
	case File::PS_UNDEFINED:
		{
			Handle_UNDEFINED(token);
		}
		break;
	
	case File::PS_SPACE:
		{
			if(token == ' ')
			{
				curSpaces++;
			}
			else
			{
				
			}
		}
		break;
	
	case File::PS_NEW_LINE:
		{
			if(token == '\n')
			{
				curNewLines++;
			}
			else
			{
				HandleUndefined(token);
			}
		}
		break;
	
	case File::PS_WORD:
		{
			
		}
		break;
	}
}

void File::Handle_UNDEFINED(const char token)
{
	if(token == ' ')
	{
		
	}
	else if(token == '\n')
	{
		currentLineNumber++;
		currentNewSpaces = 1;
		currentState = File::PS_SPACES;
	}
	else if(token == '#')
	{
		
	}
}

const std::string& File::GetPath() const
{
	return path;
}

const std::string& File::GetName() const
{
	return name;
}

bool File::IsOpened() const
{
	return isOpened;
}

bool File::HasBom() const
{
	return hasBom;
}

bool File::GetEncoding() const
{
	return encoding;
}

}

